#pragma once

#include <iostream>
#include <string>
#include <vector>
#include <mutex>
#include <fstream>
#include <assert.h>
#include <httplib.h>
#include <jsoncpp/json/json.h>
#include "../comm/util.hpp"
#include "../comm/log.hpp"
#include "./oj_model.hpp"
#include "./oj_view.hpp"

namespace oj_control
{
    using namespace oj_model;
    using namespace oj_view;
    using namespace ns_util;
    using namespace ns_log;
    using namespace httplib;

    std::string machine_list = "./conf/machine.conf";
    // 描述可以提供服务的主机
    class Machine
    {
    public:
        std::string _ip;
        int _port;
        uint64_t _load;   // 负载
        std::mutex *_mut; // 互斥锁，c++的互斥锁不允许拷贝，所以使用指针
    public:
        Machine()
            : _ip("0.0.0.0"), _port(0), _load(0)
        {
        }
        ~Machine()
        {
        }

    public:
        void IncLoad(Machine *machine)
        {
            machine->_load++;
        }
        void DecLoad(Machine *machine)
        {
            machine->_load--;
        }
    };

    // 用于控制负载均衡
    class Intelligent_Control
    {
    private:
        std::vector<Machine> _machine_list; // 能够提供服务的设备列表
        std::vector<int> _online_machine;   // 在线设备列表，用设备在设备列表的数组下标来充当设备编号
        std::vector<int> _offline_machine;  // 离线设备
        std::mutex *_mutex;

    public:
        Intelligent_Control() 
        {
            _mutex = new std::mutex;
            assert(LoadConf(machine_list));
        }
        ~Intelligent_Control()
        {
        }

    public:
        // macine_list: ip:port
        bool LoadConf(std::string &machine_list)
        {
            std::ifstream in(machine_list);
            if (!in.is_open())
            {
                LOG(FATAL) << "machine_list load error";
                return false;
            }
            std::string line;
            while (getline(in, line))
            {
                std::vector<std::string> target;
                StringUtil::SplitString(line, target, ":");
                if (target.size() != 2)
                {
                    LOG(ERROR) << "machine conf error" << std::endl;
                    continue;
                }
                Machine temp;
                temp._ip = target[0];
                temp._port = atoi(target[1].c_str());
                temp._load = 0;
                temp._mut = new std::mutex;
                _online_machine.push_back(_machine_list.size());
                _machine_list.push_back(temp);
            }
            return true;
        }

        bool IntallantChoice(int *id, Machine **machine)
        {
            _mutex->lock();
            // 选择负载最小的主机，更新其负载，并将主机返回给上层
            // 负载均衡算法：
            // 1.随机数+hash 2.轮询+hash
            int num = _online_machine.size();
            if (num == 0)
            {
                _mutex->unlock();
                LOG(ERROR) << "no machine avilable";
                return false;
            }
            // 找到负载最小的主机
            int min_load = _machine_list[_online_machine[0]]._load;
            int i = 0;
            *machine = &_machine_list[0];
            for (i = 0; i < num; i++)
            {
                if (min_load > _machine_list[_online_machine[i]]._load)
                {
                    LOG(INFO) << "online_list:" << i << std::endl;
                    min_load = _machine_list[_online_machine[i]]._load;
                    *id = _online_machine[i];
                    *machine = &_machine_list[_online_machine[i]];
                }
            }
            _mutex->unlock();
            return true;
        }

        void GoOnline(int id)
        {
        }

        void GoOffline(int id)
        {
            _mutex->lock();
            for (auto it = _online_machine.begin(); it != _online_machine.end(); it++)
            {
                if (*it == id)
                {
                    _online_machine.erase(it);
                    _offline_machine.push_back(id);
                    break;
                }
            }
            _mutex->unlock();
        }

        void ShowOnlineMachine()
        {
            _mutex->lock();
            std::cout << "online machine:";
            for (auto &it : _online_machine)
            {
                std::cout << _online_machine[it] << " ";
            }

            std::cout << "offline machine:";
            for (auto &it : _offline_machine)
            {
                std::cout << _offline_machine[it] << " ";
            }
            _mutex->unlock();
        }
    };

    class Control
    {
    private:
        Model _model;
        View _view;
        Intelligent_Control col;

    public:
        bool AllQuestions(std::string *html)
        {
            std::vector<Question> question;
            if (_model.GetAllQuestion(&question))
            {
                // 成功获取，构建html
                _view.AllQuestionsHtml(question, html);
                return true;
            }
            else
            {
                return false;
            }
        }

        bool OneQuestion(std::string num, std::string *html)
        {
            Question s;
            if (_model.GetOneQuestion(num, &s))
            {
                _view.OneQuestionHtml(s, html);
                return true;
            }
            else
            {
                return false;
            }
        }

        void Judge(std::string number, std::string in_json, std::string *out_json)
        {
            // 根据题目编号number获取题目细节
            Question q;
            _model.GetOneQuestion(number, &q);
            LOG(INFO) << "1" << std::endl;
            // in_json: code input
            // 1.对in_json进行反序列化，提取出用户提交的代码、input等
            Json::Reader reader;
            Json::Value in_value;
            reader.parse(in_json, in_value);
            std::string code = in_value["code"].asString();
            LOG(INFO) << "2" << std::endl;
            // 拼接用户提交的代码和测试用例代码
            Json::Value compile_value;
            compile_value["input"] = in_value["input"];
            compile_value["code"] = code + q._tail;
            compile_value["cpu_limit"] = q._time;
            compile_value["mem_limit"] = q._mem;
            Json::FastWriter witer;
            std::string compile_code = witer.write(compile_value);
            LOG(INFO) << "3" << std::endl;
            // 选择负载最低的主机
            // 一直选择，直到选出主机或者遍历完所有主机
            while (true)
            {
                int id = 0;
                Machine *machine = new Machine;
                if (!col.IntallantChoice(&id, &machine))
                {
                    // 遍历完所有主机但没有找到能用的就证明所有主机离线了，那么退出
                    LOG(ERROR) << "all machine can not avliable";
                    break;
                }
                LOG(INFO) << "choose successfully, id:" << id;
                // 发起http请求
                Client client(machine->_ip, machine->_port);
                machine->IncLoad(machine);
                if (auto res = client.Post("/compile_and_run", compile_code, "application/json;charset=utf-8"))
                {
                    if (res->status == 200)
                    {
                        // 将运行结果拷贝到out_json中
                        *out_json = res->body;
                        machine->DecLoad(machine);
                        LOG(INFO) << "compile and run success";
                        break;
                    }
                    // 减少负载
                    machine->DecLoad(machine);
                    // Done
                }
                else
                {
                    // 出错
                    LOG(ERROR) << "compile and run error, id:" << id;
                    // 将这个主机视为离线，加入离线队列
                    col.GoOffline(id);
                    col.ShowOnlineMachine(); // for debug
                }

                col.ShowOnlineMachine(); // for debug
            }
        }
    };
}